#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <list>
#include <cassert>
#include <utility>


using namespace std;

////////////////////////////////////////////////////////////////////////////////

const unsigned int ASCII_A = 97;
const unsigned int ASCII_Z = ASCII_A + 26;
const bool         DEBUG = false;

typedef size_t                            etat_t;
typedef unsigned char                     symb_t;
typedef set< etat_t >                     etatset_t;
typedef vector< vector< etatset_t > >     trans_t;
typedef vector< etatset_t >               epsilon_t;
typedef map< etatset_t, etat_t >          map_t;


////////////////////////////////////////////////////////////////////////////////

struct sAutoNDE
{
  // caractéristiques
  size_t nb_etats;
  size_t nb_symbs;
  size_t nb_finaux;

  etat_t initial;
  // état initial

  etatset_t finaux;
  // états finaux : finaux_t peut être un int*, un tableau dynamique comme vector<int>
  // ou une autre structure de donnée de votre choix.

  trans_t trans;
  // matrice de transition : trans_t peut être un int***, une structure dynamique 3D comme vector< vector< set<int> > >
  // ou une autre structure de donnée de votre choix.

  epsilon_t epsilon;
  // transitions spontanées : epsilon_t peut être un int**, une structure dynamique 2D comme vector< set<int> >
  // ou une autre structure de donnée de votre choix.
};

////////////////////////////////////////////////////////////////////////////////

bool FromFileTxt(sAutoNDE& at, string path);
bool FromFileJff(sAutoNDE& at, string path);

bool FromFile(sAutoNDE& at, string path)
{

  string extension;
  if (path.find_last_of(".") != std::string::npos)
    extension = path.substr(path.find_last_of(".")+1);
  else
    extension = "";

  if (extension == "txt")
    return FromFileTxt(at,path);
  if (extension == "jff")
    return FromFileTxt(at,path);

  cout << "extension de fichier non reconnue\n";
  return false;
}

bool FromFileTxt(sAutoNDE& at, string path)
{

  ifstream myfile(path.c_str(), ios::in);
  //un flux d'entree obtenu à partir du nom du fichier
  string line;
  // un ligne lue dans le fichier avec getline(myfile,line);
  istringstream iss;
  // flux associé à la chaine, pour lire morceau par morceau avec >> (comme cin)
  etat_t s(0), t(0);
  // deux états temporaires
  symb_t a(0);
  // un symbole temporaire

  if (myfile.is_open()){
    // la première ligne donne 'nb_etats nb_symbs nb_finaux'
    do{
      getline(myfile,line);
    } while (line.empty() || line[0]=='#');
    // on autorise les lignes de commentaires : celles qui commencent par '#'
    iss.str(line);
    if((iss >> at.nb_etats).fail() || (iss >> at.nb_symbs).fail() || (iss >> at.nb_finaux).fail())
        return false;
    // la deuxième ligne donne l'état initial
    do{
      getline (myfile,line);
    } while (line.empty() || line[0]=='#');
    iss.clear();
    iss.str(line);
    if((iss >> at.initial).fail())
      return -1;

    // les autres lignes donnent les états finaux
    for(size_t i = 0; i < at.nb_finaux; i++){
        do{
          getline (myfile,line);
        } while (line.empty() || line[0]=='#');
        iss.clear();
        iss.str(line);
         if((iss >> s).fail())
          continue;
//        cerr << "s= " << s << endl;
        at.finaux.insert(s);
    }

    // on alloue les vectors à la taille connue à l'avance pour éviter les resize dynamiques
    at.epsilon.resize(at.nb_etats);
    at.trans.resize(at.nb_etats);
    for(size_t i=0;i<at.nb_etats;++i)
      at.trans[i].resize(at.nb_symbs);

  // lecture de la relation de transition
    while(myfile.good()){
      line.clear();
      getline (myfile,line);
      if (line.empty() && line[0]=='#')
        continue;
      iss.clear();
      iss.str(line);

      // si une des trois lectures echoue, on passe à la suite
      if((iss >> s).fail() || (iss >> a).fail() || (iss >> t).fail() || (a< ASCII_A ) || (a> ASCII_Z ))
        continue;

      //test espilon ou non
      if ((a-ASCII_A) >= at.nb_symbs){
        cerr << "s=" << s<< ", (e), t=" << t << endl;
        //TODO remplir epsilon
      }
      else{
        cerr << "s=" << s<< ", a=" << a-ASCII_A << ", t=" << t << endl;
        //TODO remplir trans
      }
    }
    myfile.close();
    return true;
 }
  return false;
  // on ne peut pas ouvrir le fichier
}



/** ---------------------------- PORTE ALEXANDRE 11108330----------------------------------- */

// -----------------------------------------------------------------------------
//! Fonctions à compléter pour la première partie du projet
// -----------------------------------------------------------------------------

/** fonction booleenne qui charge un automate à partir de l'identifiant d'un fichier,
au format txt ou au format JFLAP. False est renvoyé en cas d'erreur de lecture; */

bool FromFileJff(sAutoNDE& at, string path) //! A faire
{
  //TODO définir cette fonction
  path="hello";
  at.nb_etats=0;
  return false;
}


//! "ContientFinal" indique si un ensemble d'états contient un des états finaux;
bool ContientFinal(const sAutoNDE& at,const etatset_t& e)
{
	for(etatset_t::iterator it_cf=e.begin(); it_cf != e.end(); it_cf++)
	{
		for(etatset_t::iterator it_automate=at.finaux.begin(); it_automate!=at.finaux.end(); it_automate++)
		{
			if(*it_cf == *it_automate)
			{
				return true;
			}
		}
	}


  return false;
}


// bool EstDeterministe(const sAutoNDE& at)  indique si l'automate est déterministe;
bool EstDeterministe(const sAutoNDE& at)
{
  unsigned int i, j;
  // On cherche a trouvé si l'automate contient des epsilon transits, alors il n'est pas déterministe.

  for(epsilon_t::const_iterator i_estDeter=at.epsilon.begin(); i_estDeter!=at.epsilon.end(); i_estDeter++)
  {
     if( !(*i_estDeter).empty() )
     {
        return false;
      }
   }
   /* On fait ensuite  une double  boucle dans les vectors contenus dans at.trans
     si une cas contient plus d'un élément dans l'ensemble à partir d'un état et d'une transition fixés
     alors on en déduira que l'automate n'est pas déterministe */
   for(i=0; i<at.trans[i].size(); i++)
   {
      for(j=0; j<ar.trans[i].size(); j++)
      {
          if(at.trans[i][j].size()>1)
          {
              return false;
           }
       }
    }
  // Sinon l'automate sera alors déterministe
  return true;
}


void Fermeture(const sAutoNDE& at, etatset_t& e)
{
  // Cette fonction clot l'ensemble d'états E={e_0, e_1, ... ,e_n} passé en
  // paramètre avec les epsilon transitions
   // 2 etatset de travail
    etatset_t temp1, temp2;
    // Copie de e dans un ensemble d'état temporair pour pouvoir ne pas avoir e en entré et en sortie,
    // ce qui pose des problèmes quant à l'arret de la fonction
    for(etatset_t::iterator it=e.begin(); it!=e.end(); it++)
        temp1.insert(*it);
    while(!temp1.empty())
    {
        for(etatset_t::iterator it=temp1.begin(); it!=temp1.end(); it++)
        {
            if(!at.epsilon[*it].empty())
            {
                for(etatset_t::iterator it2=at.epsilon[*it].begin(); it2!=at.epsilon[*it].end(); it2++)
                {
                    e.insert(*it2);
                    temp2.insert(*it2);
                }
            }
        }
        temp1 = temp2;
        temp2.clear();
    }


}

etatset_t Delta(const sAutoNDE& at, const etatset_t& e, symb_t c)
{
  etatset_t etat_suivant;

	// Boucle sur l'ensemble d'état en paramètre, on insère dans etat_suivant (vide au départ) toutes les transitions (sauf epsilon-transitions de at partant de e)
	for (etatset_t::iterator it=e.begin(); it!=e.end(); it++){
		if (!at.trans[*it][c-ASCII_A].empty())
			etat_suivant.insert(at.trans[*it][c-ASCII_A].begin(), at.trans[*it][c-ASCII_A].end());
	}

	// Fermeture
	Fermeture(at, etat_suivant);

	return etat_suivant;
}

//! bool Accept(const sAutoNDE&, string str) qui indique si l'automate accepte le mot ou pas

bool Accept(const sAutoNDE& at, string str)
{
   // Déclaration d'un ensemble d'état pour stocker les états etat_courants
    etatset_t etat_etat_courant;

   // Mise en place de l'état initial de at dans les états etat_courants
    etat_etat_courant.insert(at.initial);
    Fermeture(at,etat_etat_courant);

    // Lecture symbole par symbole du mot en paramètre.
    //Construction des états au fur et à mesure de la lecture du mot
    for(unsigned i=0; i<str.size(); i++)
    {
        etat_etat_courant = Delta(at,etat_etat_courant,str[i]);
     }
       // Si un des états d'état_etat_courant se trouve dans at alors le mot est accepté
      // Pour Cela on utilise la fonction ContientFinal()
    return ContientFinal(at,etat_etat_courant);
}



bool EstDeterministe(const sAutoNDE& at)
{
    unsigned int i,j;
    // Si l'automate contient des epsilon transits alors il n'est pas déterministe.
    for(epsilon_t::const_iterator it=at.epsilon.begin(); it!=at.epsilon.end(); it++)
    {
        if(!(*it).empty())
            return false;
    }
    // Double boucle dans les vectors imbriqués de at.trans.
    //Si une case contient plus d'un élément dans l'ensemble à partir d'un état et d'une transit fixés
    //alors l'automate n'est pas déterministe
    for(i=0; i<at.trans.size(); i++)
    {
        for(j=0; j<at.trans[i].size(); j++)
        {
            if (at.trans[i][j].size()>1)
            {
                return false;
            }
        }
    }
    return true;
}

void Fermeture(const sAutoNDE& at, etatset_t& e)
{
    // Cette fonction clot l'ensemble d'états E={e_0, e_1, ... ,e_n} passé en
    // paramètre avec les epsilon transits

    // 2 etatset de travail
    etatset_t temp1, temp2;
    // Copie de e dans un ensemble d'état temporair pour pouvoir ne pas avoir e en entré et en sortie,
    // ce qui pose des problèmes quant à l'arret de la fonction
    for(etatset_t::iterator it=e.begin(); it!=e.end(); it++)
        temp1.insert(*it);
    while(!temp1.empty())
    {
        for(etatset_t::iterator it=temp1.begin(); it!=temp1.end(); it++)
        {
            if(!at.epsilon[*it].empty())
            {
                for(etatset_t::iterator it2=at.epsilon[*it].begin(); it2!=at.epsilon[*it].end(); it2++)
                {
                    e.insert(*it2);
                    temp2.insert(*it2);
                }
            }
        }
        temp1 = temp2;
        temp2.clear();
    }
}


etatset_t Delta(const sAutoNDE& at, const etatset_t& e, symb_t c)
{
    // définir cette fonction en utilisant Fermeture
    // Variable à renvoyer
    etatset_t etat_etat_courant;

    // On parcours e et si le symbole c mène quelque part à partir d'un état de e, on met à jour l'etat etat_courant
    for(etatset_t::iterator it=e.begin(); it!=e.end(); it++)
    {
        if(!at.trans[*it][c-ASCII_A].empty())
            etat_etat_courant.insert(at.trans[*it][c-ASCII_A].begin(),at.trans[*it][c-ASCII_A].end());
    }

    // On clot etat_etat_courant
    Fermeture(at,etat_etat_courant);

    return etat_etat_courant;

}

bool Accept(const sAutoNDE& at, string str) //TESTER str si est dans mot acceptable
{
    // Déclaration d'un ensemble d'état pour stocker les états etat_courants
    etatset_t etat_etat_courant;

    // Mise en place de l'état initial de at dans les états etat_courants
    etat_etat_courant.insert(at.initial);
    Fermeture(at,etat_etat_courant);

    // Lecture symbole par symbole du mot en paramètre.
    //Construction des états au fur et à mesure de la lecture du mot
    for(unsigned i=0; i<str.size(); i++)
        etat_etat_courant = Delta(at,etat_etat_courant,str[i]);

    // Si un des états d'état_etat_courant se trouve dans at alors le mot est accepté
    return ContientFinal(at,etat_etat_courant);
}

////////////////////////////////////////////////////////////////////////////////

ostream& operator<<(ostream& out, const sAutoNDE& at)
{
    int i,j;
    /* Affichage du nombre d'état, de symboles et d'états finaux (première ligne) et de l'état initial (seconde ligne) */
    out << at.nb_etats << " " << at.nb_symbs << " " << at.finaux.size() << endl;
    out << at.initial << endl;

    /* Affichage des états finaux */
    for(etatset_t::iterator it=at.finaux.begin(); it!=at.finaux.end(); it++)
        out << *it << endl;

    /* Double boucle pour afficher la table des epsilon  */
    for(epsilon_t::const_iterator it=at.epsilon.begin(); it!=at.epsilon.end(); it++)
    {
        i=it-at.epsilon.begin();
        for(etatset_t::iterator it1=(*it).begin(); it1!=(*it).end(); it1++)
        {
            out<<i<<" e "<<*it1<<endl;
        }
    }

    /* Triple boucle pour afficher la table des transits  */
    for(trans_t::const_iterator it=at.trans.begin(); it!=at.trans.end(); it++)
    {
        i=it-at.trans.begin();
        for(vector<etatset_t>::const_iterator it1=(*it).begin(); it1!=(*it).end(); it1++)
        {
            j=it1-at.trans[i].begin();
            for(etatset_t::iterator it2=(*it1).begin(); it2!=(*it1).end(); it2++)
            {
                out<<i<<" "<<(char)(j+ASCII_A)<<" "<<*it2<<endl;
            }
        }
    }
    return out;
}

sAutoNDE Determinize(const sAutoNDE& at)
{

    sAutoNDE res;
    etatset_t etat;
    epsilon_t epsilon_fermeture;
    etatset_t initial,transit;
    map_t mymap;

    res.nb_symbs = at.nb_symbs;

    //Construction de la fermeture de chaque etat + tableau de vector
    //d'etat set contenant les e-fermetures pour chaqu'un etats
    epsilon_fermeture.resize(at.nb_etats);

    for(unsigned int it = 0; it<at.nb_etats; it++)
    {
        etat.insert(it);
        Fermeture(at,etat);
        epsilon_fermeture[it].insert(etat.begin(),etat.end());
        etat.clear();
    }

    //On initialise l'etat initiale du nouvel automate comme at
    res.initial = 0;
    res.nb_finaux = 0;

    //l'epsilon_fermeture de l'etat initial est le premier etat de l'automate deterministe
    initial = epsilon_fermeture[at.initial];

    //On regarde si initial est un etat final
    if(ContientFinal(at,initial))
    {
        res.finaux.insert(0);
    }

    //On initialise le premier etat du nouvel automate
    mymap.insert(mymap.end(),pair<etatset_t,etat_t>(initial,0));

    //On reserve de la memoire pour trans
    res.epsilon.resize(at.nb_etats*at.nb_etats);

    //On reserve de la memoire pour epsilon
    res.trans.resize(at.nb_etats*at.nb_etats);

    for(size_t p=0; p<(int)at.nb_etats*at.nb_etats; ++p)
    {
        res.trans[p].resize(at.nb_symbs);
    }

    queue<etatset_t> etats_file;
    etats_file.push(initial);

    etatset_t etat_courant;
    pair<map_t::iterator,bool> ret;


    /*etats_file est la liste des etats qui reste a traiter
     *Comme l'algo du cours, elle se remplie seulement avec des etats qui n'ont
     *pas encore ete traites*/

    while(!etats_file.empty())
    {

        //on récupère le premier élément de la file
        etat_courant = etats_file.front();
        //on defile le premier élément
        etats_file.pop();

        for(unsigned int i = 0; i<res.nb_symbs; i++)
        {
            transit = Delta(at,etat_courant,(char)i+ASCII_A);

            //la map ne prend encompte les doublons
            ret = mymap.insert(pair<etatset_t,etat_t>(transit,mymap.size()));

            //Si la transition n'existe pas dans la map
            if(ret.second == true)
            {
                //On l'enfile
                etats_file.push(transit);

                if(ContientFinal(at,transit))
                {
                    res.finaux.insert(mymap.find(transit)->second);
                }
            }
            res.trans[mymap.find(etat_courant)->second][i].insert(mymap.find(transit)->second);
        }
    }

    //On affecte les valeurs maintenant que le traitement est terminé
    res.nb_finaux = res.finaux.size();
    res.nb_etats = mymap.size();

    return res;

}


// -----------------------------------------------------------------------------
//! Fonctions à compléter pour la seconde partie du projet
// -----------------------------------------------------------------------------

ostream& operator<<(ostream& out, const sAutoNDE& at)
{
  //!TODO définir cette fonction

  return out;
}


bool ToGraph(sAutoNDE& at, string path)
{
  //!TODO définir cette fonction

  return true;
}


bool ToJflap(sAutoNDE& at, string path)
{
  //!TODO définir cette fonction

  return true;
}


/** fonction outil : on garde x, et on "ajoute" trans et epsilon de y
en renommant ses états, id est en décallant les indices des états de y
de x.nb_etats */
sAutoNDE Append(const sAutoNDE& x, const sAutoNDE& y)
{
  //!TODO définir cette fonction
  assert(x.nb_symbs == y.nb_symbs);
  sAutoNDE r;

  return r;
}


//! sAutoNDE Union(const sAutoNDE& x, const sAutoNDE& y) qui calcule l'union des deux automates x et y;
sAutoNDE Union(const sAutoNDE& x, const sAutoNDE& y)
{
  assert(x.nb_symbs == y.nb_symbs);

  sAutoNDE r = Append(x, y);

  // Initialisation de l'automate
  r.nb_etats=r.nb_etats+1;
  r.nb_finaux = 0;

  // On revoit la matrice de transition
  r.trans.rezise(r.nb_etats);
  r.trans[r.nb_etats-1].resize(r.nb_symbols);

  // On modifie les etats initiaux à cause des epsilon-transitions
  r.epsilon.resize(r.nb_etats);
  r.epsilon[r.nb_etats-1].insert(x.initial);
  r.epsilon[r.nb_etats-1].insert(y.initial + x.nb_etats);
  r.initial = r.nb_etats-1;

  // On insere les états finaux de l'automate x dans l'automate r que l'on retourne
  for (set<size_t>::iterator i_x_finaux = x.finaux.begin(); i_x_finaux != x.finaux.end(); i_x_finaux ++)
  {
    r.finaux.insert(*i_x_finaux); // insertion des finaux recupérer de l'automate x

    r.nb_finaux++;
  }

  // On insere les états finaux de l'automate y (en Donnee Resultat) dans l'automate r que l'on retourne
  for (set<size_t>::iterator i_y_finaux=y.finaux.begin() ; i_y_finaux != y.finaux.end(); i_y_finaux ++)
  {
    r.finaux.insert(*i_y_finaux + x.nb_etats);

    r.nb_finaux++;
  }

  return r;
}





sAutoNDE Concat(const sAutoNDE& x, const sAutoNDE& y)
{
  //TODO définir cette fonction
  assert(x.nb_symbs == y.nb_symbs);
  sAutoNDE r = Append(x, y);

  // ON initialise r
  r.nb_finaux=0;

  // On ajoute les états finaux de l'automate y modifiés par la fonction Append
  for (set<size_t>::iterator i_concat = y.finaux.begin(); i_concat != y.finaux.end(); i_concat++)
  {
     r.finaux.insert(i_concat + x.nb_etats);
     r.nb_finaux++;
  }

  // On cree une nouvelle table de transition
  for (set<size_t>::iterator i_reconstruction=x.finaux.begin(); i_reconstruction != x.finaux.end(); i_reconstruction)
  {
    r.epsilon[*i_reconstruction].insert(y.initial + x.nb_etats);
  }

  return r;
}



/** sAutoNDE Complement(const sAutoNDE& x) qui calcule le complément de l'automate x; */
sAutoNDE Complement(const sAutoNDE& x)
{
    sAutoNDE test;

    // On determinise l'automate à partir de x
    test = Determinize(x);

    // On defini les parametres
    size_t nb_etat_final=0;
    etatset_t etat_finaux;
     etatset_t tab_etats;

     // Complément des états finaux (en comptant le nombre d'état final pour l'affectation)
     for (size_t i_complement=0; i_complement < test.nb_etats ; i_complement++)
     {
	    tab_etats.insert((etat_t) i_complement);

		if ( !ContientFinal(test,tab_etats) )
		{
			etat_finaux.insert((etat_t) i_complement);
			nb_etat_final++;
		}

	    tab_etats.clear();
     }

     // On recupere les etats finaux dans notre automate, pour avoir son complement
      test.nb_finaux = nb_etat_final;
      test.finaux = etat_finaux;

       return test;
}



sAutoNDE Kleene(const sAutoNDE& x)
{
  sAutoNDE resultat_Kleene = x;

  // Ajout d'epsilon pour l'etat initial de x
  for (set<size_t>::iterator i_epsilon = x.finaux.begin(); i_epsilon != x.finaux.end(); i_epsilon++)
  {
    resultat_Kleene.epsilon[*i_epsilon].insert(x.initial);
  }

  // On redimensionne epsilon
	resultat_Kleene.epsilon.resize(resultat_Kleene.nb_etats+1);
	resultat_Kleene.epsilon[resultat_Kleene.nb_etats].insert(x.initial);

	//met a jour les etats
	resultat_Kleene.nb_etats++;
	resultat_Kleene.initial = resultat_Kleene.nb_etats - 1;


	// Pour eviter les problemes de surcharge
     resultat_Kleene.trans.resize(resultat_Kleene.nb_etats);

	 for (size_t i_redimensionne=0; i_redimensionne < resultat_Kleene.nb_etats ; i_redimensionne++ )
	 {
        resultat_Kleene.trans[i_redimensionne].resize(x.nb_symbs);
	 }
	// Le nouvel état devient final
	resultat_Kleene.finaux.insert(resultat_Kleene.nb_etats - 1);
	resultat_Kleene.nb_finaux++;

    return resultat_Kleene;

}



/** Intersection avec la loi de De Morgan,
L'opérateur Intersection correspond
 à l'application de la loi de De Morgan : l'intersection est le complémentaire de l'union des complémentaires.
 L'implémentation de cette opération nécessite donc d'implémenter les opérateurs Union et Complement.
*/
sAutoNDE Intersection(const sAutoNDE& x, const sAutoNDE& y)
{
  /* LOI DE MORGAN : x /\ y = Complement(x) U Complement(y) */

  return Complement(Union (Complement(x),Complement(y))   );


}


//! L'opérateur Produit consiste à construire l'automate produit vu en cours.
//! L'opérateur Produit consiste à construire l'automate produit vu en cours.
sAutoNDE Produit(const sAutoNDE& x, const sAutoNDE& y)
{
  //TODO définir cette fonction

  sAutoNDE r;

  return r;
}


sAutoNDE Minimize(const sAutoNDE& at)
{
  //!TODO définir cette fonction

  assert(EstDeterministe(at));
  sAutoNDE r;

  return r;
}

 //! ---------------------------------------------- Autres ////////////

void Help(ostream& out, char *s)
{
  out << "Utilisation du programme " << s << " :" << endl ;
  out << "-acc ou -accept Input Word:\n\t détermine si le mot Word est accepté" << endl;
  out << "-det ou -determinize Input :\n\t déterminise Input" << endl;
  out << "-cup ou -union Input1 Input2 :\n\t calcule l'union" << endl;
  out << "-cat ou -concat Input1 Input2 :\n\t calcul la concaténation" << endl;
  out << "-star ou -kleene Input :\n\t calcul de A*" << endl;
  out << "-bar ou -complement Input :\n\t calcul du complément" << endl;
  out << "-cap ou -intersection Input1 Input2 :\n\t calcul de l'intersection par la loi de De Morgan" << endl;
  out << "-prod ou -produit Input1 Input2 :\n\t calcul de l'intersection par construction de l'automate produit" << endl;
/*
  out << "-expr2aut ou expressionrationnelle2automate ExpressionRationnelle :\n\t calcul de l'automate correspondant à l'expression rationnelle" << endl;
*/
  out << "-min ou -minimisation Input :\n\t construit l'automate standard correspondant à Input" << endl;
  out << "-nop ou -no_operation Input :\n\t ne rien faire de particulier" << endl;

  out << "-o ou -output Output :\n\t écrire le résultat dans le fichier Output, afficher sur STDOUT si non spécifié" << endl;
  out << "-g ou -graphe :\n\t l'output est au format dot/graphiz" << endl  << endl;

  out << "Exemple '" << s << " -determinize auto.txt -output determin -g'" << endl;
  out << "Exemple '" << s << " -minimisation test.jff -output min -j'" << endl;
}


int main(int argc, char* argv[] )
{
  if(argc < 3){
    Help(cout, argv[0]);
    return EXIT_FAILURE;
  }

  int pos;
  int act=-1;                 // pos et act pour savoir quelle action effectuer
  int nb_files = 0;           // nombre de fichiers en entrée
  string str, in1, in2, out, acc, expr;
  // chaines pour (resp.) tampon; fichier d'entrée Input1; fichier d'entrée Input2;
  // fichier de sortie et chaine dont l'acceptation est à tester
  bool toFile=false, graphMode=false, jflapMode=false;     // sortie STDOUT ou fichier ? Si fichier, format graphviz ? Jflap ?

  // options acceptées
  const size_t NBOPT = 14;
  string aLN[] = {"accept", "determinize", "union", "concat", "kleene", "complement", "intersection", "produit", "expressionrationnelle2automate", "minimisation", "no_operation", "output", "graph", "jflap"};
  string aSN[] = {"acc", "det", "cup", "cat", "star", "bar", "cap", "prod", "expr2aut", "min", "nop", "o", "g", "j"};

  // on essaie de "parser" chaque option de la ligne de commande
  for(int i=1; i<argc; ++i){
    if (DEBUG) cerr << "argv[" << i << "] = '" << argv[i] << "'" << endl;
    str = argv[i];
    pos = -1;
    string* pL = find(aLN, aLN+NBOPT, str.substr(1));
    string* pS = find(aSN, aSN+NBOPT, str.substr(1));

    if(pL!=aLN+NBOPT)
      pos = pL - aLN;
    if(pS!=aSN+NBOPT)
      pos = pS - aSN;

    if(pos != -1){
      // (pos != -1) <=> on a trouvé une option longue ou courte
      if (DEBUG) cerr << "Key found (" << pos << ") : " << str << endl;
      switch (pos) {
        case 0: //acc
          in1 = argv[++i];
          acc = argv[++i];
	  nb_files = 1;
          break;
        case 1: //det
          in1 = argv[++i];
	  nb_files = 1;
          break;
        case 2: //cup
          in1 = argv[++i];
          in2 = argv[++i];
	  nb_files = 2;
          break;
        case 3: //cat
          in1 = argv[++i];
          in2 = argv[++i];
	  nb_files = 2;
          break;
        case 4: //star
          in1 = argv[++i];
	  nb_files = 1;
          break;
        case 5: //bar
          in1 = argv[++i];
	  nb_files = 1;
          break;
        case 6: //cap
          in1 = argv[++i];
          in2 = argv[++i];
	  nb_files = 2;
          break;
        case 7: //prod
          in1 = argv[++i];
          in2 = argv[++i];
	  nb_files = 2;
          break;
        case 8: //expr2aut
          expr = argv[++i];
	  nb_files = 0;
          break;
        case 9: //min
          in1 = argv[++i];
	  nb_files = 1;
          break;
        case 10: //nop
          in1 = argv[++i];
	  nb_files = 1;
          break;
        case 11: //o
          toFile = true;
          out = argv[++i];
          break;
        case 12: //g
          graphMode = true;
          break;
        case 13: //j
          jflapMode = true;
          break;
        default:
          return EXIT_FAILURE;
        }
    }
    else{
      cerr << "Option inconnue "<< str << endl;
      return EXIT_FAILURE;
    }

    if(pos<11){
      if(act > -1){
        cerr << "Plusieurs actions spécififées"<< endl;
        return EXIT_FAILURE;
      }
      else
        act = pos;
    }
  }

  if (act == -1){
    cerr << "Pas d'action spécififée"<< endl;
    return EXIT_FAILURE;
  }

/* Les options sont OK, on va essayer de lire le(s) automate(s) at1 (et at2)
et effectuer l'action spécifiée. Atr stockera le résultat*/

  sAutoNDE at1, at2, atr;

  if ((nb_files == 1 or nb_files == 2) and !FromFile(at1, in1)){
    cerr << "Erreur de lecture " << in1 << endl;
    return EXIT_FAILURE;
  }
  if (nb_files ==2 and !FromFile(at2, in2)){
    cerr << "Erreur de lecture " << in2 << endl;
    return EXIT_FAILURE;
  }

  switch(act) {
  case 0: //acc
    cout << "'" << acc << "' est accepté: " << Accept(at1, acc) << endl;
    atr = at1;
    break;
  case 1: //det
    atr = Determinize(at1);
    break;
  case 2: //cup
    atr =  Union(at1, at2);
    break;
  case 3: //cat
    atr =  Concat(at1, at2);
    break;
  case 4: //star
    atr =  Kleene(at1);
    break;
  case 5: //bar
    atr =  Complement(at1);
    break;
  case 6: //cap
    atr =  Intersection(at1, at2);
    break;
  case 7: //prod
    atr =  Produit(at1, at2);
    break;
  case 8: //expr2aut
/*
    atr =  ExpressionRationnelle2Automate(expr);
*/
    break;
  case 9: //minimisation
    atr =  Minimize(at1);
    break;
  case 10: //nop
    atr = at1;
    break;
  default:
    return EXIT_FAILURE;
  }

  // on affiche le résultat ou on l'écrit dans un fichier
  if(!toFile)
    cout << atr;
  else{
    if(graphMode){
      ToGraph(atr, out + ".gv");
      system(("dot -Tpng " + out + ".gv -o " + out + ".png").c_str());
    }
    if(jflapMode){
      ToJflap(atr, out + ".jff");
    }
    ofstream f((out + ".txt").c_str(), ios::trunc);
    if(f.fail())
      return EXIT_FAILURE;
    f << atr;
  }

  return EXIT_SUCCESS;
}



